package com.xinhe.web.service;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.xinhe.web.common.MenuComparator;
import com.xinhe.web.common.Result;
import com.xinhe.web.entity.*;
import com.xinhe.web.enums.AuthorityConstant;
import com.xinhe.web.reposity.AuthMenuRepository;
import com.xinhe.web.reposity.AuthRoleRepository;
import com.xinhe.web.response.MemberRecommendationDetailsView;
import com.xinhe.web.response.MenuAuthorityView;
import com.xinhe.web.response.MenuView;
import com.xinhe.web.util.Snowflake;
import com.xinhe.web.vo.MenuTreeView;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.inject.Provider;
import javax.persistence.EntityManager;
import java.util.*;

@Service
public class MenuService {
    //查询工厂
    private JPAQueryFactory queryFactory;
    @Autowired
    private Provider<EntityManager> entityManager;
    @PostConstruct
    public void init() {
        queryFactory = new JPAQueryFactory(entityManager);
    }
    @Autowired
    AuthMenuRepository authMenuRepository;
    @Autowired
    AuthRoleRepository authRoleRepository;

    public Result findTreeMenuByRoleIdIn(Long... roleIds) {
        List<Menu> menus = this.authMenuRepository.findByRoleIdIn(roleIds);
        List<MenuTreeView> treeMenus = convertTreeMenu(menus);
        return Result.ok(treeMenus);
    }
    private List<MenuTreeView> convertTreeMenu(List<Menu> menus) {
        Map<Long, MenuTreeView> menuMap = new HashMap<>();
        List<MenuTreeView> menuViews = new ArrayList<>();
        for (Menu menu : menus) {
            MenuTreeView view = new MenuTreeView(menu);
            menuViews.add(view);
            menuMap.put(menu.getId(), view);
        }
        menuViews.sort(new MenuComparator());
        List<MenuTreeView> treeMenus = new ArrayList<>();
        for (MenuTreeView menu : menuViews) {
            if (Objects.isNull(menu.getfParentId()) || menu.getfParentId().equals(AuthorityConstant.MENU_TOP_ID)) {
                treeMenus.add(menu);
                continue;
            }
            MenuTreeView parent = menuMap.get(menu.getfParentId());
            if (null != parent) {
                parent.addChild(menu);
            }
        }
        return treeMenus;
    }

    public Result findTreeMenuByMenuId(Long menuId) {
        Menu menu = this.authMenuRepository.findById(menuId).get();
        List<Menu> menus = this.authMenuRepository.findByTerminal(menu.getTerminal());
        List<MenuTreeView> treeMenus = convertTreeMenu(menus);
        MenuTreeView tree = findTree(treeMenus, menuId);
        for (MenuTreeView t : treeMenus) {
            if (!t.getId().equals(tree.getId())) {
                t.setChildren(null);
            }
        }
        return Result.ok(treeMenus);
    }

    private MenuTreeView findTree(List<MenuTreeView> treeMenus, Long menuId) {
        for (MenuTreeView tree : treeMenus) {
            if (tree.getId().equals(menuId)) {
                return tree;
            }
            if (CollectionUtils.isNotEmpty(tree.getChildren())) {
                MenuTreeView t = findTree(tree.getChildren(), menuId);
                if (null != t) {
                    return tree;
                }
            }
        }
        return null;
    }
    public Result<MenuAuthorityView> findTreeMenuForAuthority(Long roleId) {
        Role role = this.authRoleRepository.findById(roleId).get();
        List<Menu> menus = this.authMenuRepository.findByTerminal(role.getTerminal());
        List<MenuTreeView> treeMenus = convertTreeMenu(menus);
        List<Menu> ownMenus = this.authMenuRepository.findByRoleIdIn(new Long[] { roleId });
        List<Long> ownMenuLeafIds = new ArrayList<>();
        List<Long> ownMenuBranchIds = new ArrayList<>();
        Set<Long> parentIds = new HashSet<>();
        for (Menu menu : ownMenus) {
            parentIds.add(menu.getfParentId());
        }
        for (Menu menu : ownMenus) {
            if (!parentIds.contains(menu.getId())) {
                ownMenuLeafIds.add(menu.getId()); continue;
            }
            ownMenuBranchIds.add(menu.getId());
        }
        MenuAuthorityView view = new MenuAuthorityView();
        view.setMenus(treeMenus);
        view.setOwnMenuLeafIds(ownMenuLeafIds);
        view.setOwnMenuBranchIds(ownMenuBranchIds);
        return Result.ok(view);
    }

    /*public ListResult<MenuTreeView> findTreeMenuByRoleIdIn(Long... roleIds) {
        List<Menu> menus = this.menuDao.findByRoleIdIn(roleIds);
        List<MenuTreeView> treeMenus = convertTreeMenu(menus);
        return new ListResult(treeMenus);
    }*/
    /*private List<MenuTreeView> convertTreeMenu(List<Menu> menus) {
        Map<Long, MenuTreeView> menuMap = new HashMap<>();
        List<MenuTreeView> menuViews = new ArrayList<>();
        for (Menu menu : menus) {
            MenuTreeView view = new MenuTreeView(menu);
            menuViews.add(view);
            menuMap.put(menu.getId(), view);
        }
        menuViews.sort(new MenuComparator());
        List<MenuTreeView> treeMenus = new ArrayList<>();
        for (MenuTreeView menu : menuViews) {
            if (null == menu.getParentId() || menu.getParentId() == AuthorityConstant.MENU_TOP_ID) {
                treeMenus.add(menu); continue;
            }
            MenuTreeView parent = menuMap.get(menu.getParentId());
            if (null != parent) {
                parent.addChild(menu);
            }
        }

        return treeMenus;
    }*/
    public QueryResults<MenuView> pageMenu(Long parentId,Long roleId,Integer terminal,int pageNum, int pageSize) {
        QMenu qMenu = QMenu.menu;
        QRoleMenu qRoleMenu = QRoleMenu.roleMenu;
        QMenu qMenu2 = new QMenu("qMenu2");
        /*BooleanBuilder builder = new BooleanBuilder();
        OrderSpecifier<Date> sort = qMember.createTime.desc();
        builder.and(qMember.removed.eq(0));
        if (StringUtils.isNotEmpty(queryWord)) {
            builder.and(qMember.name.contains(queryWord).or(qMember.mobile.contains(queryWord)));
        }
        if (!Objects.isNull(startTime) && !Objects.isNull(endTime)) {
            builder.and(qMember.createTime.between(startTime, endTime));
        }
        return queryFactory.select(
                        Projections.bean(
                                MenuView.class,
                                qMenu.id
                        )
                ).from(qMenu)
                .leftJoin(qAssetDetail).on(qMember.fUserId.eq(qAssetDetail.fUserId).and(qAssetDetail.removed.eq(0)))
                .where(builder).offset(pageNum * pageSize).limit(pageSize).orderBy(sort).fetchResults();
        */
        // 构建条件
        BooleanExpression whereCondition = qMenu.isNotNull();
        if (parentId != null) {
            whereCondition = whereCondition.and(qMenu.fParentId.eq(parentId));
        }
        if (terminal != null) {
            whereCondition = whereCondition.and(qMenu.terminal.eq(terminal));
        }
        if (roleId != null) {
            queryFactory.selectFrom(qMenu).
                    innerJoin(qRoleMenu).on(qMenu.id.eq(qRoleMenu.menuId).and(qRoleMenu.removed.eq(0)))
                    .fetchAll();
        }
        QueryResults<MenuView> menuViewQueryResults = queryFactory.select(
                Projections.bean(
                        MenuView.class,
                        qMenu.id,
                        qMenu.routerName,
                        qMenu.icon,
                        qMenu.sequence,
                        qMenu.component,
                        qMenu.type,
                        qMenu.params,
                        qMenu.path,
                        qMenu.fParentId,
                        qMenu.terminal,
                        qMenu.remark,
                        qMenu2.id.count().as("childAmount")
                )
                )
                .from(qMenu)
                .leftJoin(qMenu2)
                .where(whereCondition)
                .groupBy(qMenu.id,qMenu.routerName,qMenu.icon,qMenu.title,qMenu.sequence,qMenu.component,qMenu.type,qMenu.params,qMenu.path,qMenu.fParentId,qMenu.terminal,qMenu.remark)
                .offset(pageNum * pageSize)
                .limit(pageSize)
                .fetchResults();
        return menuViewQueryResults;
    }

    public Result<Menu> findById(Long menuId) {
        return Result.ok(authMenuRepository.findById(menuId).get());
    }

    public Result saveModel(Long id, String routerName, String icon, String title, Integer sequence, String component, Integer type, String params, String path, Long parentId, Integer terminal, String remark) {
        Menu menu = new Menu();
        menu.setId(Snowflake.generateId());
        menu.setRouterName(routerName);
        menu.setIcon(icon);
        menu.setTitle(title);
        menu.setSequence(sequence);
        menu.setComponent(component);
        menu.setType(type);
        menu.setParams(params);
        menu.setPath(path);
        menu.setfParentId(parentId);
        menu.setTerminal(terminal);
        menu.setRemark(remark);
        authMenuRepository.save(menu);
        return Result.ok();
    }

    public Result delete(Long menuId) {
        authMenuRepository.deleteById(menuId);
        return Result.ok();
    }
}
